package com.lai.shop.service.impl;


import cn.hutool.core.util.ObjectUtil;
import com.lai.shop.constant.CommonConstant;
import com.lai.shop.constant.RedisConstant;
import com.lai.shop.enums.LoginTypeEnum;
import com.lai.shop.enums.StatusCodeEnum;
import com.lai.shop.exception.BizException;
import com.lai.shop.strategy.context.SocialLoginStrategyContext;
import com.lai.shop.utils.IpUtils;
import com.lai.shop.utils.PageUtils;
import com.lai.shop.utils.Query;
import com.lai.shop.vo.UserLoginVo;
import com.lai.shop.vo.UserRegisterVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Map;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.alibaba.cloud.context.utils.StringUtils;


import com.lai.shop.dao.UserDao;
import com.lai.shop.entity.UserEntity;
import com.lai.shop.service.UserService;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;


@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {


    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    SocialLoginStrategyContext socialLoginStrategyContext;

    @Autowired
    private HttpServletRequest request;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        String key = (String) params.get("key");
        String loginType = (String) params.get("loginType");
        QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(key)) {
            /*
select * from `user`
where id = ?
or username like '%?%'
or nickname like '%?%'
or mobile = ?
or email = ?;*/
            queryWrapper.eq("id", key)
                    .or().like("username", key)
                    .or().like("nickname", key)
                    .or().eq("mobile", key)
                    .or().eq("email", key);
        }

        if (!StringUtils.isEmpty(loginType)) {
            queryWrapper.eq("login_type", loginType);
        }


        /*.eq();*/
        IPage<UserEntity> page = this.page(
                new Query<UserEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void adminSaveUser(UserEntity user) {
        setUserDetail(user);

        user.setLoginType(LoginTypeEnum.LOCAL.getType());

        baseMapper.insert(user);
    }


    @Override
    public boolean register(UserRegisterVo userRegisterVo) {

        //获取redis中的code
        String redisCode = redisTemplate.opsForValue().get(RedisConstant.SMS_PREFIX + userRegisterVo.getMobile());
        //1、判断用户填写的验证码是否正确
        if (!StringUtils.isEmpty(redisCode)) {
            String redisCodePre = redisCode.split("_")[0];
            if (userRegisterVo.getCode().equals(redisCodePre)) {//验证通过
                //查询用户名是否存在      查询手机号是否存在
                UserEntity username = baseMapper.selectOne(new QueryWrapper<UserEntity>().eq("username", userRegisterVo.getUsername()));
                if (username != null) {
                    throw new BizException(StatusCodeEnum.USERNAME_EXIST);
                }
                UserEntity mobile = baseMapper.selectOne(new QueryWrapper<UserEntity>().eq("mobile", userRegisterVo.getMobile()));
                if (mobile != null) {
                    throw new BizException(StatusCodeEnum.MOBILE_EXIST);
                }
                //密码进行加密存储
                /**
                 * 当时看到使用 BCryptPasswordEncoder 时，同样的密码可以生成不同的 密文 而且还可以通过 matches 方法进行匹配验证，觉得很神奇。
                 * 后来经过调试发现，密文中本身包含了很多信息，包括 salt 和 使用 salt 加密后的 hash。因为每次的 salt 不同，因此每次的 hash 也不同。
                 * 这样就可以使得相同的 明文 生成不同的 密文，而密文中包含 salt 和 hash，因此验证过程和生成过程也是相同的
                 */
                BCryptPasswordEncoder bCrypt = new BCryptPasswordEncoder();
                //$2a$10$qGWe2gDMlvCUJYPxcHEEZ.LeP6ITNPBzL7m1k7hPApDkSMaE45vK2
                //其中：$是分割符，无意义；2a是bcrypt加密版本号；10是cost的值(默认值)；而后的前22位是salt值；再然后的字符串就是密码的密文了。
                String encodePassword = bCrypt.encode(userRegisterVo.getPassword());

                //对象属性设置
                UserEntity userEntity = new UserEntity();
                userEntity.setUsername(userRegisterVo.getUsername());
                userEntity.setPassword(encodePassword);
                userEntity.setMobile(userRegisterVo.getMobile());
                userEntity.setAvatar(CommonConstant.DEFAULT_AVATAR);
                userEntity.setLoginType(LoginTypeEnum.LOCAL.getType());
                setUserDetail(userEntity);

                int insert = baseMapper.insert(userEntity);
                if (insert > 0) {
                    //TODO 插入成功   删除redis中的验证码
                    return true;
                } else {
                    return false;
                }

            }
        }
        return false;
    }

    @Override
    public UserEntity login(UserLoginVo userLoginVo) {
        //根据用户名和手机号查询用户是否存在
        UserEntity userEntity = baseMapper.selectOne(new QueryWrapper<UserEntity>().eq("username", userLoginVo.getAccount())
                .or().eq("mobile", userLoginVo.getAccount()));
        if (ObjectUtil.isNotNull(userEntity)) {
            //有这个用户   开始验证密码验证密码
            //在加密的时候，先随机获取salt，然后跟password进行hash。
            // 在下一次校验的时候，从hash中取出salt，salt跟password进行hash，得到的结果跟保存在在数据库的hash进行比较。
            if(userEntity.getIsDisable() == CommonConstant.FALSE){
                BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
                //matches(CharSequence rawPassword: 源密码, String encodedPassword 数据库加密的密码)
                boolean matches = passwordEncoder.matches(userLoginVo.getPassword(), userEntity.getPassword());
                if (matches) {
                    //密码匹配成功
                    return userEntity;
                } else {
                    throw new BizException(StatusCodeEnum.PASSWORD_MATCHES_ERROR);
                }
            }else{
                throw new BizException(StatusCodeEnum.USER_FORBIDDEN_ERROR);
            }
        } else {
            throw new BizException(StatusCodeEnum.USERNAME_NOT_EXIST);
        }
    }




    private void setUserDetail(UserEntity user) {
        String ipAddress = IpUtils.getIpAddress(request);
        String ipSource = IpUtils.getIpSource(ipAddress);
        user.setIsDisable(CommonConstant.FALSE);
        user.setIsDelete(CommonConstant.FALSE);
        if(ipAddress.equals("0:0:0:0:0:0:0:1")){
            ipAddress = CommonConstant.DEFAULT_IP_ADDRESS;
            ipSource = CommonConstant.DEFAULT_IP_SOURCE;
        }
        user.setIpAddress(ipAddress);
        user.setIpSource(ipSource);
    }


}
